import { createElement, ReactElement, useEffect, useMemo, useState } from 'react'
import { View } from '@tarojs/components'

import models, { IVolunteerInfo, UserModel, WrapModel } from '@/models'
import { handleUserRouterError } from '@/utils'

export function useUser() {
  return [models.user] as const;
}

export function useApply() {
  return [models.apply] as const;
}

export function useUserInfo() {
  const [user] = useUser();

  return [user.userInfo] as const;
}

type IWithUserOptions = {
  /** 自动登录 */
  auto?: boolean;
  /** 兜底组件 */
  fallback?: ReactElement;
  /** 是否注入全局状态 */
  inject?: boolean;
  /** 钩子 */
  hooks?: {
    before?: () => void;
    after?: () => void;
  };
};

export type IWithUserProps<P = {}> = P & {
  user: UserModel;
};

export function withUser(options?: IWithUserOptions) {
  const { auto, fallback, inject = true, hooks = {} } = options ?? {};

  return function (Component: any) {
    const WrappedComponent = (props: any) => {
      const [inited, setInited] = useState(false);
      const [user] = useUser();

      useEffect(() => {
        const fn = async () => {
          if (auto && !user.isLogined) {
            hooks?.before?.();
            try {
              await user.autoLogin();
              setInited(true);
            } catch (err) {
              handleUserRouterError(err);
            } finally {
              hooks?.after?.();
            }
          } else {
            setInited(true);
          }
        };

        fn();
      }, []);

      if (!inited) {
        return fallback ?? createElement(View, { className: 'no-user' });
      }

      return createElement(inject ? WrapModel(Component) : Component, {
        ...props,
        user,
      });
    };

    return WrapModel(WrappedComponent);
  };
}

type IWithAuthOptions = {
  /** 兜底组件 */
  fallback?: ReactElement;
  /** 是否注入全局状态 */
  inject?: boolean;
  /** 自定义权限校验 */
  checkAuth?: (userInfo?: IVolunteerInfo) => boolean;
  /** 钩子 */
  hooks?: {
    onCheck?: (authorized?: boolean) => void;
  };
};

export function withAuth(options?: IWithAuthOptions) {
  const {
    fallback,
    inject = true,
    checkAuth = () => true,
    hooks = {},
  } = options ?? {};

  return function (Component: any) {
    const WrappedComponent = (props: any) => {
      const [user] = useUser();

      const authorized = useMemo(
        () =>
          !!user.userInfo?.isValid &&
          !!user.userInfo?.isVerify &&
          checkAuth(user.userInfo),
        [user.userInfo],
      );

      useEffect(() => {
        hooks?.onCheck?.(authorized);
      }, [authorized]);

      if (!authorized) {
        return fallback ?? createElement(View, { className: 'no-auth' });
      }

      return createElement(inject ? WrapModel(Component) : Component, props);
    };

    return WrapModel(WrappedComponent);
  };
}

